using System;
using System.Linq;
using System.Data.Entity;
using Pay.DTO;
using System.Collections.Generic;
using Pay.Data;
using Pay.Service.Cache;
using Pay.Core;
using Pay.IService;
using Pay.TO;

namespace Pay.Service {
    public class FeatureService : ServiceBase, IFeatureService, IDisposable {
        private IUnitOfWork _iUnitOfWork;
        private IManageService _iManageService;

        public FeatureService(IUnitOfWork iUnitOfWork, IManageService iManageService)
            : base(iUnitOfWork) {
            _iUnitOfWork = iUnitOfWork;
            _iManageService = iManageService;
        }

        /// <summary>
        /// 查询所有，按顺序
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public IEnumerable<feature_dto> Get(FeatureType type) {
            var filter = PredicateBuilder.True<Feature>();
            if (type != FeatureType.None)
                filter = filter.And(c => c.Type == type);
            return context.Features.Where(filter).FromCache(query => {
                var list = query.ProjectTo<Feature, feature_dto>();
                var frists = list.Where(c => c.parent_id == 0).OrderBy(c => c.sort);
                List<feature_dto> result = new List<feature_dto>();
                foreach (var item in frists) {
                    result.Add(item);
                    setChilds(list, result, item.id);
                }
                return result;
            });
        }
        private void setChilds(IEnumerable<feature_dto> list, List<feature_dto> result, long id) {
            var childs = list.Where(c => c.parent_id == id).OrderBy(c => c.sort);
            foreach (var item in childs) {
                result.Add(item);
                setChilds(list, result, item.id);
            }
        }

        public feature_dto Get(long id) {
            return context.Features.Where(c => c.Id == id).FromCacheFirstOrDefault(query => {
                var dto = query.FirstOrDefault().ToDtoObject<feature_dto>();
                dto.children = context.Features.Where(c => c.ParentId == dto.id).ProjectTo<Feature, feature_dto>();
                return dto;
            }, "feature");
        }

        /// <summary>
        /// 查询所有、将子类放在children中
        /// </summary>
        /// <param name="type"></param>
        /// <param name="depath">读取到第N级</param>
        /// <returns></returns>
        public IEnumerable<feature_dto> Get(FeatureType type, int depath = 0) {
            //string key = "feature-" + type.ToString();
            //var manager = Locator.Current.Resolve<CacheManager>();
            //var cacheKey = new CacheKey(key, new string[] { "feature" });
            //var result = manager.GetOrAdd(
            //    cacheKey,
            //    k => {
            //        var filter = PredicateBuilder.True<Feature>();
            //        if (type != FeatureType.None)
            //            filter = filter.And(c => c.Type == (int)type);
            //        var list = context.Features.Where(filter).ToDtoList<feature_dto>();
            //        var dto = list.Where(c => c.parent_id == 0).OrderBy(c => c.sort);
            //        getChilds(list, dto);
            //        return dto;
            //    }, CachePolicy.Default
            //) as IEnumerable<feature_dto>;

            //return result;
            var filter = PredicateBuilder.True<Feature>();
            if (type != FeatureType.None)
                filter = filter.And(c => c.Type == type);
            if (depath > 0)
                filter = filter.And(c => c.Depth <= depath);
            return context.Features.Where(filter).FromCacheFirstOrDefault(query => {
                var list = query.ProjectTo<Feature, feature_dto>();
                var result = list.Where(c => c.parent_id == 0).OrderBy(c => c.sort);
                getChilds(list, result);
                return result.ToList();
            }, "feature");
        }
        private void getChilds(IEnumerable<feature_dto> list, IEnumerable<feature_dto> child) {
            foreach (var item in child) {
                var c1 = list.Where(c => c.parent_id == item.id).OrderBy(c => c.sort).ToList();
                item.children = c1;
                if (c1.Count > 0)
                    getChilds(list, c1);
            }
        }

        public int Add(feature_dto dto) {
            var model = dto.ToDtoObject<Feature>();
            if (model.ParentId == 0) {
                model.Depth = 1;
                model.Path = string.Empty;
            } else {
                var parent = context.Features.FindById(model.ParentId);
                model.Depth = parent.Depth + 1;
                model.Path = string.IsNullOrEmpty(parent.Path) ? parent.Id.ToString() : string.Format("{0}-{1}", parent.Path, parent.Id);
            }
            var prev_max_sort = context.Features.Where(c => c.ParentId == model.ParentId).Select(c => (int?)c.Sort).Max();
            model.Sort = dto.sort > 0 ? dto.sort : (prev_max_sort ?? -1) + 1;
            context.Features.Add(model);
            int result = _iUnitOfWork.Commit("feature");
            //if (result > 0) {
            //    if (dto.children.Count > 0) {
            //        dto.children.ForEach((item, i) => {
            //            var child = item.ToDtoObject<Feature>();
            //            child.Type = model.Type;
            //            child.ParentId = model.Id;
            //            child.Depth = model.Depth + 1;
            //            child.Path = string.IsNullOrEmpty(model.Path) ? model.Id.ToString() : string.Format("{0}-{1}", model.Path, model.Id);
            //            child.Sort = i;
            //            context.Features.Add(child);
            //        });
            //        _iUnitOfWork.Commit("feature");
            //    }
            //}
            return result;
        }

        public int Update(feature_dto dto) {
            if (dto.id == dto.parent_id)
                throw new ServerErrorException("上级不能是本身");
            var model = context.Features.FindById(dto.id);
            var old_depth = model.Depth;
            string old_path = string.IsNullOrEmpty(model.Path) ? string.Empty : string.Format("-{0}-", model.Path);
            bool unchanged_Parent = dto.parent_id == model.ParentId;
            dto.ToDtoObject(model);

            if (!unchanged_Parent) {
                if (model.ParentId == 0) {
                    model.Depth = 1;
                    model.Path = string.Empty;
                } else {
                    var parent = context.Features.FindById(model.ParentId);
                    if (parent == null)
                        throw new ServerErrorException("找不到上级");
                    if (string.Format("-{0}-", parent.Path).Contains(string.Format("-{0}-", model.Id)))
                        throw new ServerErrorException(string.Format("{0}原来是{1}的下级，不能调整", parent.Name, model.Name));
                    model.Depth = parent.Depth + 1;
                    model.Path = string.IsNullOrEmpty(parent.Path) ? parent.Id.ToString() : string.Format("{0}-{1}", parent.Path, parent.Id);
                }
                //修改下级的Path
                var childs = (from item in context.Features
                              where ("-" + item.Path + "-").Contains("-" + model.Id + "-")
                              select item).ToList();
                foreach (var child in childs) {
                    if (old_depth > model.Depth) {
                        //升级,更新数据的新path是空时，子集的旧path用-换，否则旧path换更新数据的新path
                        bool is_frist = string.IsNullOrEmpty(model.Path);
                        child.Path = string.Format("-{0}-", child.Path).Replace(old_path, is_frist ? "-" : string.Format("-{0}-", model.Path)).Trim('-');
                    } else if (old_depth == model.Depth) {
                        //平移,用旧path换新path
                        child.Path = string.Format("-{0}-", child.Path).Replace(old_path, string.Format("-{0}-", model.Path)).Trim('-');
                    } else {
                        //降级，更新数据的旧path是空时时，子集的旧path前加更新数据的新path
                        if (string.IsNullOrEmpty(old_path))
                            child.Path = model.Path + "-" + child.Path;
                        else
                            child.Path = string.Format("-{0}-", child.Path).Replace(old_path, string.Format("-{0}-", model.Path)).Trim('-');
                    }
                    child.Depth = child.Path.Split('-').Length + 1;
                }
            }
            //if (dto.children.Count > 0) {
            //    List<Feature> req_children = new List<Feature>();
            //    dto.children.ForEach((item, i) => {
            //        var child = item.ToDtoObject<Feature>();
            //        child.Type = model.Type;
            //        child.ParentId = model.Id;
            //        child.Depth = model.Depth + 1;
            //        child.Path = string.IsNullOrEmpty(model.Path) ? model.Id.ToString() : string.Format("{0}-{1}", model.Path, model.Id);
            //        child.Sort = i;
            //        if (child.Id == 0) child.Id = DtoHelper.GuidToLongID();
            //        req_children.Add(child);
            //    });
            //    var data_children = context.Features.Where(c => c.ParentId == model.Id).ToList();
            //    var req_ids = req_children.Select(c => c.Id).CopyToList();
            //    var data_ids = data_children.Select(c => c.Id).CopyToList();
            //    //新增
            //    req_ids.Except(data_ids).DataForEach(filter_id => {
            //        var item = req_children.First(c => c.Id == filter_id);
            //        context.Features.Add(item);
            //    });
            //    //删除
            //    data_ids.Except(req_ids).DataForEach(filter_id => {
            //        var item = data_children.First(c => c.Id == filter_id);
            //        context.Entry(item).State = EntityState.Deleted;
            //    });
            //    //更新
            //    req_ids.Intersect(data_ids).DataForEach(filter_id => {
            //        var item = req_children.First(c => c.Id == filter_id);
            //        var data_item = data_children.First(c => c.Id == filter_id);
            //        data_item.Type = item.Type;
            //        data_item.Name = item.Name;
            //        data_item.Url = item.Url;
            //        data_item.Remark = item.Remark;
            //        data_item.Depth = item.Depth;
            //        data_item.Path = item.Path;
            //        data_item.Sort = item.Sort;
            //        context.Entry(data_item).State = EntityState.Modified;
            //    });
            //} else {
            //    var data_children = context.Features.Where(c => c.ParentId == model.Id).ToList();
            //    if (data_children.Count > 0)
            //        data_children.DataForEach(item => { context.Entry(item).State = EntityState.Deleted; });
            //}

            int result = _iUnitOfWork.Commit("feature");
            return result;
        }

        public int Update(long id, int sort) {
            var model = context.Features.FindById(id);
            model.Sort = sort;
            int result = _iUnitOfWork.Commit("feature");
            return result;
        }

        public int Delete(long id) {
            var model = context.Features.FindById(id);
            var data_children = context.Features.Where(c => c.ParentId == model.Id).ToList();
            if (data_children.Count > 0) {
                data_children.DataForEach(item => {
                    var item_children = context.Features.Where(c => c.ParentId == item.Id).Count();
                    if (item_children > 0)
                        throw new ServerErrorException("子功能多层级关系，不可以删除");
                    context.Entry(item).State = EntityState.Deleted;
                });
            }
            context.Features.Remove(model);
            int result = _iUnitOfWork.Commit("feature");
            return result;
        }

        /// <summary>
        /// 平台管理员的权限
        /// </summary>
        /// <param name="role_id"></param>
        /// <returns></returns>
        public IEnumerable<feature_dto> GetByAdmin(long? role_id, FeatureType featureType = FeatureType.None) {
            if (role_id.HasValue) {
                return context.RoleFeatures.Where(c => c.RoleId == role_id && c.Feature.Depth <= 3 && c.Feature.Type == featureType).FromCache(query => {
                    var list = query.Select(c => c.Feature).ProjectTo<Feature, feature_dto>();
                    var result = list.Where(c => c.parent_id == 0).OrderBy(c => c.sort);
                    getChilds(list, result);
                    return result.ToList();
                }, "feature");
            }
            return Get(featureType, 3);
        }
    }
}
